/************************************************************************

								Heroes III
					  Copyright 1999, The 3DO Company

	------------------------------------------------------------------
  						 ref_pointer.h

	$Header: /Heroes4/ref_pointer.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( SHARED_PTR_H_INCLUDED )
#define SHARED_PTR_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <cstddef>
#include <memory>

// -------------------------------------------------------------------------------------------
// base class for reference counted pointers
// -------------------------------------------------------------------------------------------
class t_shared_ptr_base
{
public:
	// a "const" reference pointer can alter these two items, because
	// adding or removing a reference may alter the list, even though
	// it doesn't alter the pointed-to object.
	mutable const t_shared_ptr_base* m_last;
	mutable const t_shared_ptr_base* m_next;
//
//	void * m_data;
};

// -------------------------------------------------------------------------------------------
// template for type-safe usage of reference counted pointers
// -------------------------------------------------------------------------------------------
template <class type>
class t_shared_ptr : public t_shared_ptr_base
{
public:
	// constructors / destructors (more below in templated area)
	         t_shared_ptr();
	explicit t_shared_ptr( type* data ); // construct from a newly allocated object
	         t_shared_ptr( std::auto_ptr< type > data );

	~t_shared_ptr();
	
    // explicitly clear all references to this object and delete it.
	//void                  free();

	type*                 get() const;
	// Releases ownership of the object and if this is the last reference
	// returns a std::auto_ptr<> to it, otherwise returns NULL.
	std::auto_ptr< type > release();         
	// Returns true if this is the only t_shared_ptr to the object
	bool                  unique() const;
	
	t_shared_ptr& operator=( type* data );   // assign a newly allocated object
	t_shared_ptr& operator=( std::auto_ptr< type > data );
	type*         operator->() const;
	type&         operator*() const;
	bool          operator!() const;

	// templated constructors - must be inlined here, due to MSC limitations.

	// construct pointer from another templated ref pointer, without typecast.
	template <class arg_type>
	t_shared_ptr( const t_shared_ptr<arg_type>& arg )
	{
		set( arg.get() );
		add_link( arg );
	}

	t_shared_ptr( const t_shared_ptr& arg );

	// same, but with a typecast.
	template <class arg_type>
	t_shared_ptr( const type*, const t_shared_ptr<arg_type>& arg )
	{
		set( static_cast<type*>( arg.get() ) );
		add_link( &arg );
	}

	// assign and typecast a reference counted pointer
	template <class arg_type>
	void assign( const t_shared_ptr<arg_type>& arg )
	{
		assign( static_cast<arg_type*>( arg.get() ), arg );
	}

	// assign but don't typecast a reference counted pointer
	template <class arg_type>
	t_shared_ptr& operator=( const t_shared_ptr<arg_type>& arg )
	{
		assign( arg.get(), arg );
		return *this;
	}

	t_shared_ptr& operator=( const t_shared_ptr& arg );

	bool operator==( const type* right ) const
	{
		return get() == right;
	}

	bool operator!=( const type* right ) const
	{
		return get() != right;
	}


protected:
	void set( type * new_data );
	void add_link( const t_shared_ptr_base& arg ) const; // link to another list
	void assign( type* data, const t_shared_ptr_base& arg );
	void construct( type* data );  // construct a new reference count for 
	                                             // a newly-allocated object.

	type *	m_data;
};

// -------------------------------------------------------
// inline implementations of functions for ref_ptr template
//---------------------------------------------------------

template <class type> 
inline t_shared_ptr<type>::t_shared_ptr()
{
	set( 0 );
}

template <class type> 
inline t_shared_ptr<type>::t_shared_ptr( type* data )
{
	construct( data );
}

template <class type> 
inline t_shared_ptr<type>::t_shared_ptr( std::auto_ptr< type > data )
{
	construct( data.release() );
}

template <class type>
inline t_shared_ptr< type >::t_shared_ptr( const t_shared_ptr& arg )
{
	set( arg.get() );
	add_link( arg );
}

// delete object if this is the last reference
template <class type> 
inline t_shared_ptr<type>::~t_shared_ptr()
{
	if (get() != 0)
	{
		if (m_last == this)
		{
			// note that deleting m_data may delete the current object,
			// so it is important not to read or write any data members afterward.
			type* data = get();

			set( 0 );
			delete data;
		}
		else
		{
			// unlink this pointer from the list of referring pointers
			m_next->m_last = m_last;
			m_last->m_next = m_next;
			set( 0 );
		}
	}
}

// -------------------------------------------------------
// free all references to this object
// -------------------------------------------------------
/*template <class type> 
inline void t_shared_ptr<type>::free()
{
	// explicitly clear all references to this object and delete it.
	if (get() != 0)
	{
		// clear all other pointers in the list
		while (m_next != this)
		{
			m_next->m_data = 0;
			m_next = m_next->m_next;
		}
		// this operation may delete the current object.
		type* data = get();

		set( 0 );
		delete data;
	}
}
*/
// -------------------------------------------------------
// assign a newly allocated object
// -------------------------------------------------------
template <class type> 
inline t_shared_ptr<type>& t_shared_ptr<type>::operator=( type* data )
{
	if (get() == 0)
	{
		construct( data );
	}
	else
	{
		t_shared_ptr_base const* last = m_last;
		t_shared_ptr_base const* next = m_next;
		type*                      old_data = get();

		construct( data );

		if (last == this)
		{
			// deleting old_data may delete the current object,
			// so it is important not to read or write any data members afterward.
			delete old_data;
		}
		else
		{
			// unlink this pointer from the list of referring pointers
			next->m_last = last;
			last->m_next = next;
		}
	}
	return *this;
}

template <class type> 
inline t_shared_ptr<type>& t_shared_ptr<type>::operator=( std::auto_ptr< type > data )
{
	return operator=( data.release() );
}

// -----------------------------------------------------
// Returns the data pointer with an appropriate type cast
// -----------------------------------------------------
template <class type>
inline type* t_shared_ptr<type>::get() const
{
	return m_data;
}

// -----------------------------------------------------
// Returns true if this is the only t_shared_ptr to the object
// -----------------------------------------------------
template < typename type >
inline bool t_shared_ptr< type >::unique() const
{
	return m_last == this || get() == 0;
}

// -----------------------------------------------------
// Releases ownership of the object and if this is the last reference
// returns a std::auto_ptr to it, otherwise returns NULL.
// -----------------------------------------------------
template < typename type >
inline std::auto_ptr< type > t_shared_ptr< type >::release()
{
	if ( get() == 0 )
		return std::auto_ptr< type >();

	if ( m_last == this )
	{
		std::auto_ptr< type > result( get() );
		set( 0 );
		return result;
	}

	m_next->m_last = m_last;
	m_last->m_next = m_next;
	set( 0 );
	return std::auto_ptr< type >();
}

template <class type>
inline type* t_shared_ptr<type>::operator->() const
{ 
	return get(); 
}

template <class type>
inline type& t_shared_ptr<type>::operator*() const
{	
	return *get(); 
}

template <class type>
inline bool t_shared_ptr<type>::operator!() const
{	
	return get() != 0; 
}

// -----------------------------------------------------
// Sets a new data pointer
// -----------------------------------------------------
template < class type >
inline void t_shared_ptr< type >::set( type* new_data )
{
	m_data = new_data;
}

// -----------------------------------------------------
// default copy assignment operator
// -----------------------------------------------------
template < class type >
inline t_shared_ptr< type >& t_shared_ptr< type >::operator=( const t_shared_ptr& arg )
{
	assign( arg.get(), arg );
	return *this;
}

// -----------------------------------------------------
// assign an existing reference-counted object to this one.
// -----------------------------------------------------
template <class type>
inline void t_shared_ptr<type>::assign( type* data, const t_shared_ptr_base& arg )
{
	if (this == &arg)
		return;
	if (get() == 0)
	{
		set( data );
		add_link( arg );
	}
	else
	{
		// Unlink this pointer from its current list and remember if we were
		// the last pointer in the current list.
		bool delete_old = false;
		if ( m_last != this )
		{
			m_next->m_last = m_last;
			m_last->m_next = m_next;
		}
		else
			delete_old = true;

		type* old_data = get();

		set( data );
		add_link( arg );

		if ( delete_old )
			delete old_data;
	}
}

// -----------------------------------------------------
// link to other pointers 
// -----------------------------------------------------
template <class type>
inline void t_shared_ptr<type>::add_link( const t_shared_ptr_base& arg ) const
{
	if (get() != 0)
	{
		m_next = &arg;
		m_last = arg.m_last;
		arg.m_last->m_next = this;
		arg.m_last = this;
	}
}

// -----------------------------------------------------
// construct a new reference count for a newly-allocated object.
// -----------------------------------------------------
template <class type>
inline void t_shared_ptr<type>::construct( type* data )
{
	set( data );
	m_last = this;
	m_next = this;
}

// -----------------------------------------------------
// equal / not equal operators
// -----------------------------------------------------
template <class type1>
inline bool operator==( const type1* left, const t_shared_ptr<type1>& right )
{
	return left == right.get();
}

template <class type1>
inline bool operator!=( const type1* left, const t_shared_ptr<type1>& right )
{
	return left != right.get();
}

template <class type1, class type2>
inline bool operator==( const t_shared_ptr<type1>& left, const type2* right )
{
	return left.get() == right;
}

template <class type1>
inline bool operator!=( const t_shared_ptr<type1>& left, const type1* right )
{
	return left.get() != right;
}

template <class type1, class type2>
inline bool operator==( const t_shared_ptr<type1>& left, const t_shared_ptr<type2>& right )
{
	return left.get() == right.get();
}

template <class type1, class type2>
inline bool operator!=( const t_shared_ptr<type1>& left, const t_shared_ptr<type2>& right )
{
	return left.get() != right.get();
}

// -------------------------------------------------------------------------------------------
// template for type-safe usage of reference counted pointers to arrays
// -------------------------------------------------------------------------------------------
template <class type>
class t_shared_array : public t_shared_ptr_base
{
public:
	// constructors / destructors (more below in templated area)
	         t_shared_array();
	explicit t_shared_array( type* data ); // construct from a newly allocated array
	t_shared_array( const t_shared_array& arg );

	~t_shared_array();
	
	//void           free();                          // explicitly clear all references 
	                                                // to this object and delete it.
	type*          get() const;
	
	t_shared_array& operator=( type* data );   // assign a newly allocated object
	type &          operator[]( ptrdiff_t index ) const;
	bool            operator!() const;

	t_shared_array& operator=( const t_shared_array& arg );

	bool operator==( const type* right ) const
	{
		return get() == right;
	}

	bool operator!=( const type* right ) const
	{
		return get() != right;
	}

protected:
	void set( type * new_data );
	void add_link( const t_shared_ptr_base& arg ) const; // link to another list
	void assign( type* data, const t_shared_ptr_base& arg );
	void construct( type* data );  // construct a new reference count for 
	                                             // a newly-allocated array.

	type *	m_data;
};

// -------------------------------------------------------
// inline implementations of functions for ref_ptr template
//---------------------------------------------------------

template <class type> 
inline t_shared_array<type>::t_shared_array()
{
	set( 0 );
}

template <class type> 
inline t_shared_array<type>::t_shared_array( type* data )
{
	construct( data );
}

template <class type>
inline t_shared_array< type >::t_shared_array( const t_shared_array& arg )
{
	set( arg.get() );
	add_link( arg );
}

// delete array if this is the last reference
template <class type> 
inline t_shared_array<type>::~t_shared_array()
{
	if (get() != 0)
	{
		if (m_last == this)
		{
			// note that deleting m_data may delete the current object,
			// so it is important not to read or write any data members afterward.
			type* data = get();

			set( 0 );
			delete[] data;
		}
		else
		{
			// unlink this pointer from the list of referring pointers
			m_next->m_last = m_last;
			m_last->m_next = m_next;
			set( 0 );
		}
	}
}

// -------------------------------------------------------
// free all references to this array
// -------------------------------------------------------
/*template <class type> 
inline void t_shared_array<type>::free()
{
	// explicitly clear all references to this object and delete it.
	if (get() != 0)
	{
		// clear all other pointers in the list
		while (m_next != this)
		{
			m_next->m_data = 0;
			m_next = m_next->m_next;
		}
		// this operation may delete the current object.
		type* data = get();

		set( 0 );
		delete[] data;
	}
}
*/
// -------------------------------------------------------
// assign a newly allocated array
// -------------------------------------------------------
template <class type> 
inline t_shared_array<type>& t_shared_array<type>::operator=( type* data )
{
	if (get() == 0)
	{
		construct( data );
	}
	else
	{
		t_shared_ptr_base const* last = m_last;
		t_shared_ptr_base const* next = m_next;
		type*                      old_data = get();

		construct( data );

		if (last == this)
		{
			// deleting old_data may delete the current object,
			// so it is important not to read or write any data members afterward.
			delete[] old_data;
		}
		else
		{
			// unlink this pointer from the list of referring pointers
			next->m_last = last;
			last->m_next = next;
		}
	}
	return *this;
}

// -----------------------------------------------------
// Returns the data pointer with an appropriate type cast
// -----------------------------------------------------
template <class type>
inline type* t_shared_array<type>::get() const
{
	return static_cast< type * >( m_data );
}

template < typename type >
inline type & t_shared_array< type >::operator[]( ptrdiff_t index ) const
{
	return get()[ index ];
}

template <class type>
inline bool t_shared_array<type>::operator!() const
{	
	return get() != 0; 
}

// -----------------------------------------------------
// Sets a new data pointer
// -----------------------------------------------------
template < class type >
inline void t_shared_array< type >::set( type* new_data )
{
	m_data = new_data;
}

// -----------------------------------------------------
// default copy assignment operator
// -----------------------------------------------------
template < class type >
inline t_shared_array< type >& t_shared_array< type >::operator=( const t_shared_array& arg )
{
	assign( arg.get(), arg );
	return *this;
}

// -----------------------------------------------------
// assign an existing reference-counted object to this one.
// -----------------------------------------------------
template <class type>
inline void t_shared_array<type>::assign( type* data, const t_shared_ptr_base& arg )
{
	if (this == &arg)
		return;
	if (get() == 0)
	{
		set( data );
		add_link( arg );
	}
	else
	{
		// Unlink this pointer from its current list and remember if we were
		// the last pointer in the current list.
		bool delete_old = false;
		if ( m_last != this )
		{
			m_next->m_last = m_last;
			m_last->m_next = m_next;
		}
		else
			delete_old = true;

		type* old_data = get();

		set( data );
		add_link( arg );

		if ( delete_old )
			delete[] old_data;
	}
}

// -----------------------------------------------------
// link to other pointers 
// -----------------------------------------------------
template <class type>
inline void t_shared_array<type>::add_link( const t_shared_ptr_base& arg ) const
{
	if (get() != 0)
	{
		m_next = &arg;
		m_last = arg.m_last;
		arg.m_last->m_next = this;
		arg.m_last = this;
	}
}

// -----------------------------------------------------
// construct a new reference count for a newly-allocated object.
// -----------------------------------------------------
template <class type>
inline void t_shared_array<type>::construct( type* data )
{
	set( data );
	m_last = this;
	m_next = this;
}

// -----------------------------------------------------
// equal / not equal operators
// -----------------------------------------------------
template <class type1>
inline bool operator==( const type1* left, const t_shared_array<type1>& right )
{
	return left == right.get();
}

template <class type1>
inline bool operator!=( const type1* left, const t_shared_array<type1>& right )
{
	return left != right.get();
}

template <class type1>
inline bool operator==( const t_shared_array<type1>& left, const type1* right )
{
	return left.get() == right;
}

template <class type1>
inline bool operator!=( const t_shared_array<type1>& left, const type1* right )
{
	return left.get() != right;
}

template <class type1, class type2>
inline bool operator==( const t_shared_array<type1>& left, const t_shared_array<type2>& right )
{
	return left.get() == right.get();
}

template <class type1, class type2>
inline bool operator!=( const t_shared_array<type1>& left, const t_shared_array<type2>& right )
{
	return left.get() != right.get();
}

#endif // !defined( SHARED_PTR_H_INCLUDED )
